package monitor

import (
	"strings"
	"time"

	"github.com/CodyGuo/dingtalk"
	"github.com/ochinchina/supervisord/config"
	"github.com/ochinchina/supervisord/util"
	log "github.com/sirupsen/logrus"
)

var dt *dingtalk.DingTalk
var tasks chan EventLog
var localIP string

func InitAlarm(c *config.Config) {
	webHook := "https://oapi.dingtalk.com/robot/send?access_token=" + c.Config.Monitor.AlarmDingtalkC0.AccessToken
	secret := c.Config.Monitor.AlarmDingtalkC0.Secret
	dt = dingtalk.New(webHook, dingtalk.WithSecret(secret))
	tasks = make(chan EventLog, 100)
	localIP = util.GetLocalIP()

	go runWorker()
}

func AddAlarm(eve EventLog) {
	tasks <- eve
}

func CloseAlarmWorker() {
	close(tasks)
}

// 每个机器人每分钟最多发送20条。如果超过20条，会限流10分钟。
// 算法如下：
//   正常每隔至少4秒钟发一条，一分钟15条
//   但是实时接收数据，4秒钟时如果有多条数据，则汇总发送
func runWorker() {
	log.Info("Dingtalk alarm 通道打开.")
	// 无限循环处理接收到的工作,可以处理完一个以后继续处理下一个
	var lastSendSec int64 = 0
	cache := make([]string, 0)
	for {
		// 从已经关闭的通道中,依然可以接收数据,并且返回一个通道类型的零值,如果一个都没接收到的时候会阻塞
		// 接收到一个以后,会继续往下执行
		task, ok := <-tasks
		// 判断通道是否清空并关闭
		if !ok {
			log.Warn("Dingtalk alarm 通道关闭.")
			// 直接退出这个goroutine
			return
		}
		// 间隔小于4秒，则添加到缓存中
		cache = append(cache, changeToAlarmText(task))
		// 大于4秒，则缓存数据+本次数据一起发送
		if time.Now().Unix()-lastSendSec >= 4 {
			var sendContent string
			n := 0
			for _, txt := range cache {
				sendContent = sendContent + txt + "\n  \n"
				n++
				if n == 4 { // 每次最多发4条
					break
				}
			}
			if err := dt.RobotSendMarkdown("事件消息", sendContent); err != nil {
				log.WithError(err).Error("send dingtalk alarm failed")
			} else {
				k := len(cache)
				if k > 40 {
					k = 40
				}
				// 清空数据，最多保留40-4条
				cache = cache[n:k]

				lastSendSec = time.Now().Unix()
			}
		}

	}
}

var alermTempl string = `<font color=Red size=4 face="黑体">Event</font>
> **时间**: Time **内容**: Content`

func changeToAlarmText(ev EventLog) string {
	et := time.Unix(ev.EventTime/1e3, 0)
	ret := strings.ReplaceAll(alermTempl, "Event", ev.Name+": "+parseType(ev.Type)+"-"+localIP)
	ret = strings.ReplaceAll(ret, "Time", et.Format("15:04:05"))
	color := "#FF0000"
	if ev.Type == "P0" {
		color = "#32CD32"
	} else if ev.Type == "P3" {
		color = "#FFD700"
	}
	ret = strings.ReplaceAll(ret, "Red", color)
	ret = strings.ReplaceAll(ret, "Content", ev.Desc+"  ")
	return ret
}

func parseType(stype string) string {
	switch stype {
	case "P0":
		return "启动成功"
	case "P1":
		return "启动最大次数失败"
	case "P2":
		return "进程非手动退出"
	case "P3":
		return "进程手动退出"
	case "A1":
		return "进程资源超限"
	case "A2":
		return "系统资源超限"
	default:
		return stype
	}
}
